package csnet.openflow.component.switchx.flowTable;

import java.io.IOException;
import java.util.Comparator;

import csnet.openflow.packet.model.Packet;
import csnet.openflow.util.NoOverridePriorityQueue;
import csnet.openflow.util.fileOperation.StreamWriter;
import csnet.openflow.util.log.LoggerGenerator;

public class LRUFlowTable implements FlowTable {
    private NoOverridePriorityQueue<Packet> flowEntries;
    private long delay;
    private long bufferTime;
    private long flowIndex;
    private StreamWriter writer;

    public LRUFlowTable(Comparator<Packet> comparator,
            long bufferTime, long delay) {
        flowEntries = new NoOverridePriorityQueue<Packet>(comparator);
        this.bufferTime = bufferTime;
        this.flowIndex = 0;
        this.delay = delay;
    }

    @Override
    public void install(Packet packet) {
        long deadline = packet.getTimestamp() + bufferTime;
        Packet original = flowEntries.put(packet, deadline);
        if (original == null) {
            packet.statistic.flowIndex = flowIndex++;
            packet.statistic.createTime = packet.getTimestamp();
            packet.statistic.deadline = deadline;
        } else {
            original.statistic.deadline = deadline;
        }
    }

    @Override
    public boolean contains(Packet packet) {
        packet.setTimestamp(packet.getTimestamp() + delay);
        Packet original = flowEntries.get(packet);
        if (original == null) {
            return false;
        }
        long deadline = packet.getTimestamp() + bufferTime;
        flowEntries.put(original, deadline);
        original.statistic.deadline = deadline;
        packet.statistic.flowIndex = original.statistic.flowIndex;
        return true;
    }

    @Override
    public void clear(long timestamp) {
        while (!flowEntries.isEmpty()) {
            Packet packet = flowEntries.peekMin();
            if (packet.statistic.deadline < timestamp) {
                flowEntries.pollMin();
                recordPacket(packet);
            } else {
                break;
            }
        }
    }

    @Override
    public int size() {
        return flowEntries.size();
    }

    public void setLogger(LoggerGenerator logger) {
        writer = logger.getBinaryLogger("flowTable");
    }

    private void recordPacket(Packet packet) {
        try {
            writer.writeLong(packet.statistic.flowIndex);
            writer.writeLong(packet.statistic.createTime);
            writer.writeLong(packet.statistic.deadline);
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}
